//
// Created by 抑~风 on 2022/12/10.
//

#include "semantic_analysis.h"
#include<string>
#include<iostream>
#include<cassert>

using namespace std;
namespace CWJ {
    namespace SEMA_ANALYSIS {
        void ASTExecutor::execute(ASTExecutor::AnnotatedTreePtr tree) {
            this->at = tree;
            this->execute(this->at->getTree());
            return;
        }

        // 执行statement语句

        void ASTExecutor::execute(ASTExecutor::ResASTNodePtr ctx) {

            if (!ctx) return;

            const string &ct = ctx->getContext();

            if (ct == "block") {
                this->executeBlock(ctx);

                return ;
            }else if(ct == "functionDeclaration"){
                return ;
            }else if(ct == "classDeclaration"){
                return ;
            }

            for(const auto & s : ctx->getChilds()){

                this->execute(s);
            }


        }

        //        statement
//        : block
//        | variableDeclarators   SEMI
//        | assginExpression SEMI
//        | expression SEMI
//        | IF parExpression statement (ELSE statement)?
//        | FOR   LPAREN   forControl   RPAREN   statement
//        | WHILE parExpression statement
//        | DO statement WHILE parExpression    SEMI
//        | RETURN expression ?    SEMI
//        | functionCall SEMI
//        | BREAK  SEMI
//        | CONTINUE  SEMI
//        | dotExpression SEMI
//        // | IDENTIFIER DOT functionCall SEMI
//        ;

        Symbol* ASTExecutor::executeStatement(ASTExecutor::ResASTNodePtr ctx) {
            if(!ctx)    return nullptr ;
            Symbol* res = nullptr;
            const string &ct = ctx->getContext();
            if (ct == "block") {
//                cout<<"execu:"<<ct<<endl;
                res = this->executeBlock(ctx);
            } else if (ct == "variableDeclarators") {
//                cout<<"exec:"<<ct<<endl;
                this->executeVarDectors(ctx);
            } else if (ct == "expression") {
//                cout<<"expression"<<ct<<endl;
                this->executeExpression(ctx);
            }else if(ct == "assginExpression"){
//                cout<<"assginExpression"<<endl;
                this->execAssginExpress(ctx);
            }
            else if (ct == "ifStatement") {
//                cout<<"exec ifStatement"<<endl;
                res =  this->execIfStatement(ctx);
            } else if (ct == "forStatement") {
//                cout<<"exec forStatement"<<endl;
                res =  this->execForStatement(ctx);
            } else if (ct == "whileStatement") {
//                cout<<"exec  while statement"<<endl;
                res = this->execWhileStatement(ctx);

            } else if (ct == "doWhileStatement") {
//                cout<<"exec doWhileStatement"<<endl;
                res = this->execDoWhileState(ctx);
            } else if (ct == "RETURN") {

            } else if (ct == "BREAK") {
                res = &Symbol::BREAK_SYMBOL;
            } else if (ct == "CONTINUE") {

//                cout<<"CONTINUE - exec"<<endl;
                res =  &Symbol::CONTINUE_SYMBOL;
            }else if(ct == "functionCall"){
//                cout<<"exec functionCall"<<endl;

                this->execFunctionCall(ctx);
            }
            else {
                for (const auto &son : ctx->getChilds()) {
                    res = this->executeStatement(son);
                    if(res == &Symbol::CONTINUE_SYMBOL || res == &Symbol::BREAK_SYMBOL)     break;
                }
            }


            return res;

        }

        Symbol* ASTExecutor::executeBlock(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return nullptr;

            Symbol* res = nullptr;

            if (ctx->getContext() == "statement") {
                res = this->executeStatement(ctx);
                return res;
            }else{
                for (const auto &s : ctx->getChilds()) {
                    res = this->executeBlock(s);
                    if(res == &Symbol::CONTINUE_SYMBOL || res == &Symbol::BREAK_SYMBOL){
                        return res;
                    }
                }
            }
            return res;
        }

        void ASTExecutor::executeVarDectors(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return;
            if (ctx->getContext() == "variableDeclarator") {
                this->executeVarDector(ctx);

                return;
            }
            for (const auto &s : ctx->getChilds()) {
                this->executeVarDectors(s);
            }
        }

        Variable *ASTExecutor::executeExpression(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return nullptr;
            Variable *var = nullptr;
            if (ctx->getContext() == "andExpression") {
                auto v = this->executeAndEpression(ctx);
                return v;
            }

            for (const auto &s : ctx->getChilds()) {
                if ((var = this->executeExpression(s))) return var;
            }

            return nullptr;
        }

        Variable *ASTExecutor::executeVarDector(ASTExecutor::ResASTNodePtr ctx) {

            Symbol *sym = this->at->getSymbolOfNode(ctx);

            if (sym->getSymbolEnum() != SymbolEnum::VARIABLE) {
                throw runtime_error("ASTExecutor::executeVarDector : variableDeclarator must var");
            }

            Variable *var = (Variable *) sym;
            Variable *initRes = this->executVariableInitializer(ctx);

            if (initRes && initRes->isValid1()) var->setVal(initRes->getVal());

            return var;
        }

        Variable *ASTExecutor::executVariableInitializer(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return nullptr;
            if (ctx->getContext() == "expression") {
                return this->executeExpression(ctx);
            }
            Variable *v = nullptr;
            for (const auto &s :ctx->getChilds()) {
                if ((v = this->executVariableInitializer(s))) return v;
            }
            return nullptr;
        }

        Variable *ASTExecutor::executeAndEpression(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return nullptr;

            Scope *cur = this->at->getScopeOfNode(ctx);
            Variable *res = new Variable("tmp", cur, ctx, &CWJType::intType, CwjAny());
            int flag = 0;
            this->executeAndExpression(ctx, *res, flag);
            auto c = res->getVal();
            return res;
        }

        // flag 0 直接赋值，1相与，3 相或
        void ASTExecutor::executeAndExpression(ASTExecutor::ResASTNodePtr ctx, Variable &res, int &flag) {
            const auto &ct = ctx->getContext();
            if (ct == "relatExpression") {
                Variable *var = this->execRelaExpress(ctx);
                if(!var || !var->isValid1())    return ;
                if (flag == 0) {
                    res.setVal(var->getVal());
                    res.setType(var->getType());
                } else {
                    CwjAny b = var->getVal(), a = res.getVal();
                    int tmp = a.cast_<int>();
                    if (flag == 1) {
                        tmp = tmp && b.cast_<int>();
                    } else if (flag == 2) {
                        tmp = tmp || b.cast_<int>();
                    }
                    res.setVal(CwjAny(tmp));
                    res.setType(var->getType());
                }

                return;
            } else if (ct == "AND_") {
                flag = 1;
            } else if (ct == "OR_") {
                flag = 2;
            }

            for (const auto &s : ctx->getChilds()) {
                this->executeAndExpression(s, res, flag);
            }

        }

        Variable *ASTExecutor::execRelaExpress(ASTExecutor::ResASTNodePtr ctx) {

            if (!ctx) return nullptr;
            Scope *cur = this->at->getScopeOfNode(ctx);
            Variable *res = new Variable("tmp", cur, ctx, &CWJType::intType, CwjAny());
            int flag = 0;
            this->execexecRelaExpress(ctx, *res, flag);

            return res;
        }

        // flag 0 直接赋值，1,<= ;2. >=  ;3 == ;4 != ;5 > ; 6 < ;
        void ASTExecutor::execexecRelaExpress(ASTExecutor::ResASTNodePtr ctx, Variable &res, int &flag) {
            const auto &ct = ctx->getContext();
            if (ct == "addExpression") {
                Variable *var = this->execAddExpress(ctx);
                if(!var || !var->isValid1())    {
                    return ;
                }
                if (flag == 0) {
                    res.setVal(var->getVal());
                    res.setType(var->getType());
                } else {
                    CwjAny a = res.getVal(), b = var->getVal();
                    int tmp = a.cast_<int>();
                    if (flag == 1) {
                        tmp = tmp <= b.cast_<int>();
                    } else if (flag == 2) {
                        tmp = tmp >= b.cast_<int>();
                    } else if (flag == 3) {
                        tmp = tmp == b.cast_<int>();
                    } else if (flag == 4) {
                        tmp = tmp != b.cast_<int>();
                    } else if (flag == 5) {
                        tmp = tmp > b.cast_<int>();
                    } else if (flag == 6) {
                        tmp = tmp < b.cast_<int>();
                    }
                    res.setVal(CwjAny(tmp));
                    res.setType(var->getType());
                }
                return;
            } else if (ct == "LE") {
                flag = 1;
            } else if (ct == "GE") {
                flag = 2;
            } else if (ct == "EQUAL") {
                flag = 3;
            } else if (ct == "NOTEQUAL") {
                flag = 4;
            } else if (ct == "GT") {
                flag = 5;
            } else if (ct == "LT") {
                flag = 6;
            }
            for (const auto &s : ctx->getChilds()) {
                this->execexecRelaExpress(s, res, flag);
            }
        }

        Variable *ASTExecutor::execAddExpress(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return nullptr;
            Scope *cur = this->at->getScopeOfNode(ctx);
            Variable *res = new Variable("tmp", cur, ctx, &CWJType::intType, CwjAny());
            int flag = 0;
            this->execAddExpress(ctx, *res, flag);
            return res;
        }

        // flag 0 赋值，1 相加 2 相减
        void ASTExecutor::execAddExpress(ASTExecutor::ResASTNodePtr ctx, Variable &res, int &flag) {
            const auto &ct = ctx->getContext();
            if (ct == "mulExpression") {
                Variable *var = this->execMulExpress(ctx);
                if(!var || !var->isValid1())    return ;
                if (flag == 0) {
                    res.setVal(var->getVal());
                    res.setType(var->getType());
                } else {
                    CwjAny b = var->getVal(), a = res.getVal();
                    int tmp = a.cast_<int>();
                    if (flag == 1) {
                        tmp = tmp + b.cast_<int>();
                    } else if (flag == 2) {
                        tmp = tmp - b.cast_<int>();
                    }
                    res.setVal(CwjAny(tmp));
                    res.setType(var->getType());
                }
                return;
            } else if (ct == "ADD") {
                flag = 1;
            } else if (ct == "SUB") {
                flag = 2;
            }

            for (const auto &s : ctx->getChilds()) {
                this->execAddExpress(s, res, flag);
            }

        }

        Variable *ASTExecutor::execDopExpress(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return nullptr;
            Scope *cur = this->at->getScopeOfNode(ctx);
            Variable *res = new Variable("tmp", cur, ctx, &CWJType::intType, CwjAny());
            int flag = 0;
            this->execDopExpress(ctx, *res, flag);
            return res;
        }
        // flag 0 直接赋值 1 取反 2 前增 3 前减  6 有值--> 6,2 ;6,3后的
        void ASTExecutor::execDopExpress(ASTExecutor::ResASTNodePtr ctx, Variable &res, int &flag) {
            if(!ctx)    return ;
            const auto &ct = ctx->getContext();
            if (ct == "primary") {
                Variable *var = this->execPrimary(ctx);
                if(!var || !var->isValid1())    return ;
                if (flag == 0) {
                    res.setVal(var->getVal());
                    res.setType(var->getType());
                } else {
                    CwjAny a = var->getVal();
                    int tmp = a.cast_<int>();
                    if (flag == 1) {
                        tmp = !tmp;
                    } else if (flag == 2) {
                        tmp = tmp + 1;
                        var->setVal(tmp);
                    }else if (flag == 3) {
                        tmp = tmp - 1;
                        var->setVal(tmp);
                    }
                    res.setVal(CwjAny(tmp));
                    res.setType(var->getType());
                }
                return;
            }else if (ct == "BING") {
                flag = 1;
            } else if (ct == "INC") {
                flag = 2;
            } else if (ct == "DEC") {
                flag = 3;
            }
            for (const auto &s : ctx->getChilds()) {
                this->execDopExpress(s, res, flag);
            }

        }

        Variable *ASTExecutor::execMulExpress(ASTExecutor::ResASTNodePtr ctx) {
            if (!ctx) return nullptr;
            Scope *cur = this->at->getScopeOfNode(ctx);
            Variable *res = new Variable("tmp", cur, ctx, &CWJType::intType, CwjAny());
            int flag = 0;
            this->execMulExpress(ctx, *res, flag);
            return res;
        }

        void ASTExecutor::execMulExpress(ASTExecutor::ResASTNodePtr ctx, Variable &res, int &flag) {
            if(!ctx)    return ;
            const auto &ct = ctx->getContext();
            if (ct == "dopExpression") {
                //TODO 錯誤
                Variable *var = this->execDopExpress(ctx);
                if(!var || !var->isValid1())    return ;
                if (flag == 0) {
                    res.setVal(var->getVal());
                    res.setType(var->getType());
                } else {
                    CwjAny b = var->getVal(), a = res.getVal();
                    int tmp = a.cast_<int>();
                    if (flag == 1) {
                        tmp = tmp * b.cast_<int>();
                    } else if (flag == 2) {
                        tmp = tmp / b.cast_<int>();
                    }else if(flag == 3){
                        tmp = tmp % b.cast_<int>();
                    }
                    res.setVal(CwjAny(tmp));
                    res.setType(var->getType());
                }
                return;
            } else if (ct == "MUL") {
                flag = 1;
            } else if (ct == "DIV") {
                flag = 2;
            }else if (ct == "MOD") {
                flag = 3;
            }

            for (const auto &s : ctx->getChilds()) {
                this->execMulExpress(s, res, flag);
            }
        }

        Variable *ASTExecutor::execPrimary(ASTExecutor::ResASTNodePtr ctx) {

            if(!ctx)    return nullptr;
            if(ctx->getContext() == "IDENTIFIER" || ctx->getContext() == "DECIMALLITERAL"){
                auto v = this->at->getSymbolOfNode(ctx);
                if(!v || v->getSymbolEnum() != SymbolEnum::VARIABLE){
                    throw runtime_error("ASTExecutor::execMulExpress : var is null");
                }
                return (Variable *) v;
            }else if(ctx->getContext() == "expression"){
                return this->executeExpression(ctx);
            }
            Variable* res = nullptr;
            for(const auto &s : ctx->getChilds()){
                if((res = this->execPrimary(s)))    return res;
            }
            return nullptr;
        }

        void ASTExecutor::execAssginExpress(ASTExecutor::ResASTNodePtr ctx) {
            if(!ctx)    return ;
            auto var = (Variable*)this->at->getSymbolOfNode(ctx);

            auto tmp = this->executeExpression(ctx);
            if(!var){
                throw(runtime_error("ASTExecutor::execAssginExpress : var is null"));
            }
            if(tmp && tmp->isValid1())  var->setVal(tmp->getVal());
            return ;
        }

        Symbol *ASTExecutor::execIfStatement(ASTExecutor::ResASTNodePtr ctx,bool& flag,bool&isEnd) {

            if(!ctx)    return nullptr;

            const auto & ct = ctx->getContext();

            Symbol* res = nullptr;

            if(ct == "parExpression"){
                if(this->execParExpress(ctx)){
                    flag = true;
                }
            }else if(ct == "statement"){
                // TODO 处理 break,continue
                if(flag)   {
                    res = this->executeStatement(ctx),isEnd = true;
                }
            }else if(ct == "ELSE"){
                flag = true;
            }else {
                for(const auto& s : ctx->getChilds()){
                    res = this->execIfStatement(s,flag,isEnd);
                    if(isEnd)   return res;
                }
            }


            return res;
        }

        bool ASTExecutor::execParExpress(ASTExecutor::ResASTNodePtr ctx) {
            auto c = this->executeExpression(ctx);
            return c ? c->getVal().cast_<int>() : true;
        }

        Symbol *ASTExecutor::execIfStatement(ASTExecutor::ResASTNodePtr ctx) {
            bool flag = false,isEnd = false;
            return this->execIfStatement(ctx,flag,isEnd);
        }

        Symbol* ASTExecutor::execWhileStatement(ASTExecutor::ResASTNodePtr ctx) {
            if(!ctx)    return nullptr;
            const auto & childs = ctx->getChilds();

            auto parCtx = childs[1],bodyCtx = childs[2];

            Symbol*res = nullptr;

            while(this->execParExpress(parCtx)){
                res = this->executeStatement(bodyCtx);
                if(res == &Symbol::BREAK_SYMBOL)    break;
                else if(res == &Symbol::CONTINUE_SYMBOL)    continue;
            }
            return nullptr;
        }

        Symbol *ASTExecutor::execDoWhileState(ASTExecutor::ResASTNodePtr ctx) {
            if(!ctx)    return nullptr;
            const auto & childs = ctx->getChilds();

            auto parCtx = childs[3],bodyCtx = childs[1];
            Symbol* res = nullptr;
            do{
                res = this->executeStatement(bodyCtx);
                if(res == &Symbol::BREAK_SYMBOL)    break;
                else if(res == &Symbol::CONTINUE_SYMBOL)    continue;
            }
            while(this->execParExpress(parCtx));
            return nullptr;
        }

        Symbol *ASTExecutor::execForStatement(ASTExecutor::ResASTNodePtr ctx) {
            if(!ctx)    return nullptr;
            vector<ResASTNodePtr>list(3,nullptr);
            const auto & childs = ctx->getChilds();
            ResASTNodePtr forCt = childs[2],forBody = childs[4];
            this->findSubForControl(forCt,list);

            const auto & initCtx = list[0],judgeCtx = list[1],updateCtx = list[2];

            Symbol* res = nullptr;
            this->execForinit(initCtx);

            while(this->execParExpress(judgeCtx)){
                res = this->executeStatement(forBody);
                this->execExpressList(updateCtx);
                if(res == &Symbol::BREAK_SYMBOL)    break;
                else if(res == &Symbol::CONTINUE_SYMBOL)    continue;

            }

            return nullptr;
        }

        void ASTExecutor::findSubForControl(ASTExecutor::ResASTNodePtr ctx, vector<ResASTNodePtr> &list) {

            if(list.size()<3)   throw runtime_error("ASTExecutor::findSubForControl : list size < 3");
            if(!ctx)    return ;
            const auto & ct  = ctx->getContext();

            if(ct == "forInit"){
                list[0] = ctx;
            }else if(ct == "expression"){
                list[1] = ctx;
            }else if(ct == "expressionList"){
                list[2] = ctx;
            }else {
                for(const auto & s : ctx->getChilds()){
                    this->findSubForControl(s,list);
                }
            }

        }

        void ASTExecutor::execForinit(ASTExecutor::ResASTNodePtr ctx) {
            if(!ctx)    return ;
            if(ctx->getContext() == "variableDeclarators"){
                this->executeVarDectors(ctx);
            }else if(ctx->getContext() == "expressionList"){
                this->execExpressList(ctx);
            }else{
                for(auto s : ctx->getChilds()){
                    this->execForinit(s);
                }
            }
        }

        void ASTExecutor::execExpressList(ASTExecutor::ResASTNodePtr ctx) {
            if(!ctx)    return ;
            if(ctx->getContext() == "expression"){
                this->executeExpression(ctx);
            }else{
                for(auto s : ctx->getChilds()){
                    this->execExpressList(s);
                }
            }
        }

        Variable* ASTExecutor::execFunctionCall(ASTExecutor::ResASTNodePtr ctx) {

            vector<Variable*>params;
            this->calcuExpressionList(ctx,params);
            FunctionType* func = (FunctionType*)this->at->getSymbolOfNode(ctx);

            if(func->getName() == FunctionType::PRINT.getName()){

                for(auto &c :params){
                    cout<<*c<<" ";
                }
            }else if(func->getName() == FunctionType::PRINTLN.getName()){
                for(auto &c :params){
                    cout<<*c<<" ";
                }
                cout<<endl;
            }else{
                return this->execFunctionCall(func,params);
            }

            return nullptr;

        }

        void ASTExecutor::calcuExpressionList(ASTExecutor::ResASTNodePtr ctx, vector<Variable *> &params) {
            if(!ctx)    return ;
            if(ctx->getContext() == "expression"){
                params.push_back(this->executeExpression(ctx));
            }else{
                for(const auto& s : ctx->getChilds() ){
                    this->calcuExpressionList(s,params);
                }
            }
        }

        Variable *ASTExecutor::execFunctionCall(FunctionType *ctx, const vector<Variable *> &params) {
            const auto& funcVars = ctx->getParamsVars();
            for(int i=0;i<params.size();i++){
                funcVars[i]->setVal(params[i]->getVal());
            }
            return (Variable* )this->executeBlock(ctx->getBody());
        }
    }
}